package com.mapbox.navigation.ui.maps.route.line.model

import android.content.Context
import android.graphics.Color
import androidx.appcompat.content.res.AppCompatResources
import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor
import com.mapbox.maps.extension.style.layers.properties.generated.IconPitchAlignment
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

class MapboxRouteLineOptionsTest {

    private val ctx: Context = mockk()

    @Before
    fun setUp() {
        mockkStatic(AppCompatResources::class)
        every { AppCompatResources.getDrawable(any(), any()) } returns mockk(relaxed = true)
    }

    @After
    fun cleanUp() {
        unmockkStatic(AppCompatResources::class)
    }

    @Test
    fun withRouteLineResourceProvider() {
        val routeLineResources = RouteLineResources.Builder().build()

        val options = MapboxRouteLineOptions.Builder(ctx)
            .withRouteLineResources(routeLineResources)
            .build()

        assertEquals(routeLineResources, options.resourceProvider)
    }

    @Test
    fun withRouteLineBelowLayerId() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .withRouteLineBelowLayerId("someLayerId")
            .build()

        assertEquals("someLayerId", options.routeLineBelowLayerId)
    }

    @Test
    fun withTolerance() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .withTolerance(.111)
            .build()

        assertEquals(.111, options.tolerance, 0.0)
    }

    @Test
    fun withRouteStyleDescriptors() {
        val routeStyleDescriptors =
            listOf(RouteStyleDescriptor("foobar", Color.CYAN, Color.YELLOW))
        val options = MapboxRouteLineOptions.Builder(ctx)
            .withRouteStyleDescriptors(routeStyleDescriptors)
            .build()

        assertEquals(routeStyleDescriptors, options.routeStyleDescriptors)
    }

    @Test
    fun displayRestrictedRoadSections() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .displayRestrictedRoadSections(true)
            .build()

        assertTrue(options.displayRestrictedRoadSections)
    }

    @Test
    fun styleInactiveRouteLegsIndependently() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .styleInactiveRouteLegsIndependently(true)
            .build()

        assertTrue(options.styleInactiveRouteLegsIndependently)
    }

    @Test
    fun displaySoftGradientForTraffic() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .displaySoftGradientForTraffic(true)
            .build()

        assertTrue(options.displaySoftGradientForTraffic)
    }

    @Test
    fun softGradientTransition() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .softGradientTransition(77)
            .build()

        assertEquals(77.0, options.softGradientTransition, 0.0)
    }

    @Test(expected = IllegalArgumentException::class)
    fun softGradientTransition_whenValueIsZero() {
        MapboxRouteLineOptions.Builder(ctx)
            .softGradientTransition(0)
            .build()
    }

    @Test
    fun vanishingRouteLineUpdateIntervalNano() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .vanishingRouteLineUpdateInterval(44L)
            .build()

        assertEquals(44, options.vanishingRouteLineUpdateIntervalNano)
    }

    @Test
    fun waypointLayerIconOffset() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .waypointLayerIconOffset(listOf(3.0, 4.4))
            .build()

        assertEquals(3.0, options.waypointLayerIconOffset.first(), 0.0)
        assertEquals(4.4, options.waypointLayerIconOffset.component2(), 0.0)
        assertEquals(2, options.waypointLayerIconOffset.size)
    }

    @Test
    fun waypointLayerIconAnchor() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .waypointLayerIconAnchor(IconAnchor.BOTTOM_LEFT)
            .build()

        assertEquals(IconAnchor.BOTTOM_LEFT, options.waypointLayerIconAnchor)
    }

    @Test
    fun iconPitchAlignment() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .iconPitchAlignment(IconPitchAlignment.AUTO)
            .build()

        assertEquals(IconPitchAlignment.AUTO, options.iconPitchAlignment)
    }

    @OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
    @Test
    fun enableSharedCache() {
        val options = MapboxRouteLineOptions.Builder(ctx)
            .shareLineGeometrySources(true)
            .build()

        assertTrue(options.shareLineGeometrySources)
    }

    @OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
    @Test
    fun toBuilder() {
        val routeLineResources = RouteLineResources.Builder().build()
        val routeStyleDescriptors =
            listOf(RouteStyleDescriptor("foobar", Color.CYAN, Color.YELLOW))

        val options = MapboxRouteLineOptions.Builder(ctx)
            .withRouteLineResources(routeLineResources)
            .withVanishingRouteLineEnabled(true)
            .withRouteLineBelowLayerId("someLayerId")
            .withTolerance(.111)
            .withRouteStyleDescriptors(routeStyleDescriptors)
            .displayRestrictedRoadSections(true)
            .styleInactiveRouteLegsIndependently(true)
            .displaySoftGradientForTraffic(true)
            .softGradientTransition(77)
            .vanishingRouteLineUpdateInterval(44L)
            .waypointLayerIconOffset(listOf(3.0, 4.4))
            .waypointLayerIconAnchor(IconAnchor.BOTTOM)
            .iconPitchAlignment(IconPitchAlignment.AUTO)
            .shareLineGeometrySources(true)
            .lineDepthOcclusionFactor(0.85)
            .build()
            .toBuilder(ctx)
            .build()

        assertEquals(routeLineResources, options.resourceProvider)
        assertEquals("someLayerId", options.routeLineBelowLayerId)
        assertNotNull(options.vanishingRouteLine)
        assertEquals(.111, options.tolerance, 0.0)
        assertEquals(routeStyleDescriptors, options.routeStyleDescriptors)
        assertTrue(options.displayRestrictedRoadSections)
        assertTrue(options.styleInactiveRouteLegsIndependently)
        assertTrue(options.displaySoftGradientForTraffic)
        assertEquals(77.0, options.softGradientTransition, 0.0)
        assertEquals(44, options.vanishingRouteLineUpdateIntervalNano)
        assertEquals(3.0, options.waypointLayerIconOffset.first(), 0.0)
        assertEquals(4.4, options.waypointLayerIconOffset.component2(), 0.0)
        assertEquals(2, options.waypointLayerIconOffset.size)
        assertEquals(IconAnchor.BOTTOM, options.waypointLayerIconAnchor)
        assertEquals(IconPitchAlignment.AUTO, options.iconPitchAlignment)
        assertEquals(0.85, options.lineDepthOcclusionFactor, 0.00001)
        assertTrue(options.shareLineGeometrySources)
    }

    @OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
    @Test(expected = IllegalArgumentException::class)
    fun lineDepthOcclusionFactorTooSmall() {
        MapboxRouteLineOptions.Builder(ctx).lineDepthOcclusionFactor(-0.1).build()
    }

    @OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
    @Test(expected = IllegalArgumentException::class)
    fun lineDepthOcclusionFactorTooBig() {
        MapboxRouteLineOptions.Builder(ctx).lineDepthOcclusionFactor(1.1).build()
    }
}
